﻿using Common.HttpHelper;
using Core.AppExtensions;
using Core.AppExtensions.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Core.QuartzService
{
    public static class QuartzExtensions
    {
        /// <summary>
        /// 注册quartz的基础服务
        /// </summary>
        /// <param name="services"></param>
        public static void AddBaseQuartzService(this IServiceCollection services)
        {
            //注册job工厂实现类 --定时任务依赖注入需要用原生注入
            services.AddSingleton<IJobFactory, QuartzFactory>();
            //注册日志实现类
            services.AddSingleton<ILogProvider, ConsoleLogProvider>();
        }
        
        /// <summary>
        ///  quartz服务注册方法(只能在程序开始时调用)
        /// </summary>
        /// <param name="services"></param>
        /// <param name="JobAndCron">需要执行的定时任务类名和定时cron表达式</param>
        public static void RegistQuartzService(this IServiceCollection services, Dictionary<Type,string> jobsAndCron)
        {
            var dictionary = GetTriggerAndJob(jobsAndCron,type=>services.AddSingleton(type));
            ServiceProvider provider = services.BuildServiceProvider();
            IScheduler scheduler = new StdSchedulerFactory().GetScheduler().GetAwaiter().GetResult();
            scheduler.JobFactory = provider.GetService<IJobFactory>();
            //接管日志(没有日志框架的情况下才通过这个方法输出日志)
            LogProvider.SetCurrentLogProvider(provider.GetService<ILogProvider>());
            scheduler.ScheduleJobs(dictionary, true);
            scheduler.Start();
        }
        /// <summary>
        /// 注册一个立即执行的任务(只能在程序启动时调用)
        /// </summary>
        /// <param name="services"></param>
        /// <param name="jobType"></param>
        public static void RegistExecuteJob(this IServiceCollection services, Type jobType)
        {
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = GetTriggerAndJob(jobType,type=>services.AddSingleton(type));
            ServiceProvider provider = services.BuildServiceProvider();
            IScheduler scheduler = new StdSchedulerFactory().GetScheduler().GetAwaiter().GetResult();
            //接管日志(没有日志框架的情况下才通过这个方法输出日志)
            LogProvider.SetCurrentLogProvider(provider.GetService<ILogProvider>());
            scheduler.ScheduleJobs(dictionary, true);
            scheduler.Start();
        }
        /// <summary>
        /// 立即执行一个任务(不能在程序启动时调用)
        /// </summary>
        /// <param name="jobType"></param>
        public static void ExcuteJob(Type jobType)
        {
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = GetTriggerAndJob(jobType);
            IScheduler scheduler = GetScheduler();
            scheduler.ScheduleJobs(dictionary, true);
            scheduler.Start();
        }
        /// <summary>
        /// 注册任务（不能在程序启动时调用）
        /// </summary>
        /// <param name="jobsAndCron"></param>
        public static void AddJob(Dictionary<Type,string> jobsAndCron)
        {
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = GetTriggerAndJob(jobsAndCron);
            IScheduler scheduler = GetScheduler();
            scheduler.ScheduleJobs(dictionary, true);
            scheduler.Start();
        }

        /// <summary>
        /// 获取IScheduler 注意此方法不能在程序启动时调用
        /// </summary>
        /// <returns></returns>
        private static IScheduler GetScheduler()
        {
            IScheduler scheduler = new StdSchedulerFactory().GetScheduler().GetAwaiter().GetResult();
            scheduler.JobFactory = App.GetService<IJobFactory>(typeof(IJobFactory));
            //接管日志(没有日志框架的情况下才通过这个方法输出日志)
            LogProvider.SetCurrentLogProvider(App.GetService<ILogProvider>(typeof(ILogProvider)));
            return scheduler;
        }
        private static Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> GetTriggerAndJob(Dictionary<Type, string> jobsAndCron,Action<Type> action=null)
        {
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>();
            //object[] parameters = new object[1] { services };
            //MethodInfo DependServicesMethod = typeof(ServiceCollectionServiceExtensions)
            //            .GetMethods()
            //            .Where(t => t.Name == "AddScoped" && t.IsStatic && t.GetParameters().Length == 1 && t.GetGenericArguments().Length == 1)
            //            .FirstOrDefault();
            foreach (Type job in jobsAndCron.Keys)
            {
                if (!typeof(IJob).IsAssignableFrom(job))
                {
                    //如果任务类不继承IJob，就不注册这个定时任务,防止报错
                    continue;
                }
                else
                {
                    #region 反射调用依赖注入方法
                    //MethodInfo GenericMethod = DependServicesMethod.MakeGenericMethod(job);
                    //GenericMethod.Invoke(null, parameters);
                    #endregion
                    if (action != null)
                    {
                        action.Invoke(job);
                    }
                    IJobDetail Job = JobBuilder.Create(job)
                        .WithIdentity($"Job:{job.Name}")
                        .Build();
                    ITrigger trigger = TriggerBuilder.Create()
                        .WithIdentity($"trigger:{job.Name}")
                        .StartNow()
                        .WithSchedule(CronScheduleBuilder.CronSchedule(jobsAndCron[job]))
                        .ForJob(Job)
                        .Build();
                    dictionary.Add(Job, new HashSet<ITrigger> { trigger });
                }
            }
            return dictionary;
        }
        private static Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> GetTriggerAndJob(Type jobType, Action<Type> action = null)
        {
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>();
            if (action != null)
            {
                action.Invoke(jobType);
            }
            IJobDetail Job = JobBuilder.Create(jobType)
                .WithIdentity($"Job:{jobType.Name}")
                .Build();
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity($"trigger:{jobType.Name}")
                .StartNow()
                .ForJob(Job)
                .Build();
            dictionary.Add(Job, new HashSet<ITrigger> { trigger });
            return dictionary;
        }

        private static Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> GetTriggerAndJob(List<Type> jobTypes, Action<Type> action = null)
        {
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>();
            foreach (var jobType in jobTypes)
            {
                if (action != null)
                {
                    action.Invoke(jobType);
                }
                IJobDetail Job = JobBuilder.Create(jobType)
                    .WithIdentity($"Job:{jobType.Name}")
                    .Build();
                ITrigger trigger = TriggerBuilder.Create()
                    .WithIdentity($"trigger:{jobType.Name}")
                    .StartNow()
                    .ForJob(Job)
                    .Build();
                dictionary.Add(Job, new HashSet<ITrigger> { trigger });
            }
            return dictionary;
        }
    }
}
